package md.frolov.bible.views;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;

import md.frolov.bible.Constants;
import md.frolov.bible.commands.GoToURLCommand;
import md.frolov.bible.editors.BibleReferenceSelection;
import md.frolov.bible.initializers.CustomXMLParser;
import md.frolov.bible.model.Archive;
import md.frolov.bible.model.CrossReferenceProvider;
import md.frolov.bible.model.Module;
import md.frolov.bible.model.Verse;

import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.ViewPart;

public class ReferenceView extends ViewPart {
	public final static String PLUGIN_ID =  "md.frolov.bible.views.ReferenceView";
	
	private StyledText styledText;
	private CrossReferenceProvider crp = CrossReferenceProvider.getInstance();
	
	private StyleRange linkStyle;
	private StyleRange mainVerseStyle;
	private StyleRange titleRefStyle;
	
	private Cursor handCursor;
	private Cursor beamCursor;
	
	private List<String> links = new ArrayList<String>();
	private List<StyleRange> styles = new ArrayList<StyleRange>();
	private List<Integer> ranges = new ArrayList<Integer>();

	private Module module = null;
	
	@Override
	public void createPartControl(Composite parent) {
		FillLayout layout = new FillLayout();
		parent.setLayout(layout);
		
		handCursor = new Cursor(getSite().getShell().getDisplay(),SWT.CURSOR_HAND);
		beamCursor = new Cursor(getSite().getShell().getDisplay(),SWT.CURSOR_IBEAM);
		styledText = new StyledText(parent, SWT.WRAP | SWT.V_SCROLL);
		styledText.setEditable(false);
		styledText.setEnabled(true);
		
		linkStyle = new StyleRange(0,0,new Color(styledText.getDisplay(),25,0,100),styledText.getBackground());
		
		mainVerseStyle = new StyleRange(0,0,styledText.getForeground(),styledText.getBackground());
		FontData fd = styledText.getFont().getFontData()[0];
		Font font = new Font(styledText.getDisplay(),fd.name,Math.round((float)(fd.height*0.9)), SWT.BOLD);
		mainVerseStyle.font = font;
//		mainVerseStyle.fontStyle = SWT.BOLD;
		
		titleRefStyle = new StyleRange(0,0,linkStyle.foreground,linkStyle.background);
		titleRefStyle.fontStyle = SWT.BOLD;
		
		hookSelectionListeners();
		hookListeners();
	}
	
	private void hookListeners(){
		styledText.addMouseListener(new MouseListener(){

			@Override
			public void mouseDown(MouseEvent e) {
				int offset;
				try{
					offset = styledText.getOffsetAtLocation(new Point(e.x,e.y));
				} catch (IllegalArgumentException ex){
					return;
				}
				
				//moving cursor at the beginning of hyperlink
				int i = getRangeAtOffset(offset);
				String ref = links.get(i);
				if(ref!=null){
					String url = module.getUrlForStringLocation(ref);
					if(url==null) return;
					styledText.setSelection(ranges.get(i*2));
					GoToURLCommand.executeStatic(url);
				}
			}

			@Override
			public void mouseUp(MouseEvent e) {

			}

			@Override
			public void mouseDoubleClick(MouseEvent e) {
				// TODO Auto-generated method stub
				
			}
		});
		
		styledText.addMouseMoveListener(new MouseMoveListener(){

			@Override
			public void mouseMove(MouseEvent e) {
				int offset;
				try{
					offset = styledText.getOffsetAtLocation(new Point(e.x,e.y));
				} catch (IllegalArgumentException ex){
					styledText.setCursor(beamCursor);
					return;
				}
				
				if(getRefAtOffset(offset)!=null){
					styledText.setCursor(handCursor);
				} else {
					styledText.setCursor(beamCursor);
				}
			}
			
		});
	}
	
	private int getRangeAtOffset(int offset){
		for(int i=0;i<styles.size();i++){
			if(offset>=ranges.get(i*2) && offset<ranges.get(i*2)+ranges.get(i*2+1)){
				return i;
			}
			if(offset<ranges.get(i*2)) break;
		}
		return -1;
	}
	
	private String getRefAtOffset(int offset){
		int ix = getRangeAtOffset(offset);
		if(ix==-1) return null;
		return links.get(ix);
	}
	
	private void hookSelectionListeners(){
		getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(new ISelectionListener(){

			@Override
			public void selectionChanged(IWorkbenchPart part, ISelection selection) {
				if(selection instanceof BibleReferenceSelection){
					String reference = ((BibleReferenceSelection)selection).getReference(); 
					Matcher m = Constants.urlPattern.matcher(reference);
					if (m.matches()){
						String moduleId = m.group(1);
						String verseId = m.group(2)+'.'+m.group(3)+'.'+m.group(4);
						Module module = Archive.getInstance().getModuleByID(moduleId);
						setInput(module, verseId);
					}
				}
			}
			
		});
	}

	@Override
	public void setFocus() {

	}

	private void clearText(){
		links.clear();
		ranges.clear();
		styles.clear();
		styledText.setText("");
	}
	
	public void setInput(Module m, String refId){
		try{
			clearText();
			List<String> listIds = crp.getReferences(refId);
			String[] ids;
			if(listIds!=null){
				ids = new String[listIds.size()+1];
				for (int i=0;i<listIds.size();i++){
					ids[i+1]=listIds.get(i);
				}
			} else
				ids = new String[1];
			ids[0] = refId;
			
			this.module  = m;
			
			CustomXMLParser cp = new CustomXMLParser(m.getFile());
			Verse[] vs = cp.getVerses(ids);
			for(int i=0;i<vs.length;i++)
				if(vs[i].getId().equals(refId)){
					setContents(vs, i);
					break;
				}
		} catch (Exception e){
			e.printStackTrace();
		}
	}
	
	private void setContents(Verse[] verses, int mainIx){
		StringBuilder sb = new StringBuilder(1024);
		Verse mainV = verses[mainIx];
		appendText(sb,mainV.getName(), titleRefStyle, mainV.getName());
		sb.appendCodePoint(Character.LINE_SEPARATOR);
		appendText(sb,mainV.getContents(),mainVerseStyle);
		
		sb.appendCodePoint(Character.LINE_SEPARATOR).appendCodePoint(Character.LINE_SEPARATOR);
		
		if(verses.length==1){
			sb.append("Nothing found");
			setStyledTextContents(sb);
			return;
		}
		
		for(int i=0;i<verses.length;i++){
			if(i==mainIx) continue;
			if(verses[i]==null){
				appendText(sb,"<<null>>",linkStyle,"");
				sb.appendCodePoint(Character.LINE_SEPARATOR).appendCodePoint(Character.LINE_SEPARATOR);
				continue;
			}
			appendText(sb, verses[i].getName(), linkStyle, verses[i].getName());
			sb.append(" ");
			sb.append(verses[i].getContents());
			sb.appendCodePoint(Character.LINE_SEPARATOR).appendCodePoint(Character.LINE_SEPARATOR);
		}
		setStyledTextContents(sb);
	}
	
	private void setStyledTextContents(StringBuilder sb){
		styledText.setText(sb.toString());
		
		int[] r = new int[ranges.size()];
		StyleRange[] sr = new StyleRange[styles.size()];
		for(int i=0;i<sr.length;i++){
			sr[i] = styles.get(i);
			r[i*2] = ranges.get(i*2);
			r[i*2+1] = ranges.get(i*2+1);
		}
		
		styledText.setStyleRanges(r, sr);
	}
	
	private void appendText(StringBuilder sb, String text,StyleRange style){
		appendText(sb, text, style, null);
	}
	
	private void appendText(StringBuilder sb, String text, StyleRange style, String ref){
		ranges.add(sb.length());
		styles.add(style);
		ranges.add(text.length());
		links.add(ref);
		sb.append(text);
	}
}
